#include "uart.h"
#include "shell.h"
#include "io.h"
#include "queue.h"
#include "unistd.h"
#include "shell.h"
#include "debug.h"
#include "semaphore.h"

#define UART_DEVICE_NAME            "uart1"

#define USART_IT_ORE_FLAG           (8UL)

static queue_t uart1_queue;
static sem_t uart1_sem;

static err_t uart_open (struct stdops *ops)
{
    sem_init(&uart1_sem, 0, QUEUE_BUFFER_MAX);
    
    return 0;
}

static ssize_t uart_aio_read ( struct stdops *ops, char __user *buffer, size_t size, loff_t loff )
{
    os_u32 data;
    char __user *recv_buffer = buffer;
    
    sem_wait(&uart1_sem);
    
    while (!queue_pop(&uart1_queue, &data) && size)
    {
        size--;
        *recv_buffer++ = data;
    }
  
    return recv_buffer - buffer;
}

static ssize_t uart_aio_write ( struct stdops *ops, const char __user *buffer, size_t size, loff_t loff )
{
    size_t len = size;
  
    while (len--)
    {
        while (USART_GetFlagStatus(USART_PRINT, USART_FLAG_TC) == RESET)
        { ; }
  
        USART_SendData(USART_PRINT, *buffer++);
    }
  
    return size;
}

static ssize_t uart_read ( struct stdops *ops, char __user *buffer, size_t size, loff_t loff )
{
    os_u32 data;
    char __user *recv_buffer = buffer;
    
    sem_wait(&uart1_sem);
    
    while (!queue_pop(&uart1_queue, &data) && size)
    {
        size--;
        *recv_buffer++ = data;
    }
  
    return recv_buffer - buffer;
}

static ssize_t uart_write ( struct stdops *ops, const char __user *buffer, size_t size, loff_t loff )
{   
    size_t len = size;
  
    while (len--)
    {
        while (USART_GetFlagStatus(USART_PRINT, USART_FLAG_TC) == RESET)
        { ; }
  
        USART_SendData(USART_PRINT, *buffer++);
    }
  
    return size;
}

void USART_IRQ_HANDLE(void)
{
    char data = (char)USART_PRINT->DR;
  
    if( USART_PRINT->SR & USART_IT_ORE_FLAG )
    {
        volatile char ruin;
    
        ruin = USART_PRINT->SR;
        ruin = USART_PRINT->CR1;
    }

    queue_push(&uart1_queue, data);
    sem_post(&uart1_sem);
}

static struct stdops uart1_ops = 
{
    .open  = uart_open,
    .read  = uart_read,
    .write = uart_write,
};

err_t uart_configuration(void)
{
    err_t err;
    
    err = stdobj_create(STDOBJ_DEVICE, "uart1", &uart1_ops);
    ASSERT_KERNEL(err == 0);
    
    shell_dev_create("uart1", &err, SHELL_DEVICE_ACK);
    ASSERT_KERNEL(err == 0);

    return 0;
}